# 微信API接口调用

import datetime
import json
import logging
import uuid
from unicodedata import decimal

import requests
from django.conf import settings
from django.http import JsonResponse
from wechatpayv3 import SignType, WeChatPay, WeChatPayType

from mars.apps.systems.models import WechatConfig as WeConfig
# from mars.utils.common import singleton
from mars.utils.exceptions.enums import StatusCodeEnum
from mars.utils.exceptions.exceptions import APIException

LOGGER = logging.getLogger("django")



class WeChat:
    def __init__(self):
        self.__weconfig = WeConfig.objects.all().first()
        self.__wxpay = self.__init_wxpay()

    def __init_wxpay(self):
        # 微信支付商户号（直连模式）或服务商商户号（服务商模式，即sp_mchid)
        MCHID = self.__weconfig.mch_id

        # 商户证书私钥
        with open(self.__weconfig.mch_private_key, "r") as f:
            PRIVATE_KEY = f.read()
        
        # 商户证书序列号
        CERT_SERIAL_NO = self.__weconfig.mch_serialno

        # API v3密钥，
        APIV3_KEY = self.__weconfig.apiv3_key

        # APPID，应用ID或服务商模式下的sp_appid
        APPID = self.__weconfig.app_id

        # 回调地址，也可以在调用接口的时候覆盖
        domain = self.__weconfig.domain
        if domain.find("https") < 0 and domain.find("http") < 0:
            domain = "".join(["https://", domain])
        NOTIFY_URL = "%s/api/wechat/notify/" % domain

        # 初始调试时可不设置，调试通过后再设置，示例值:'./cert'
        CERT_DIR = None

        # 初始化
        return WeChatPay(
            wechatpay_type=WeChatPayType.MINIPROG,  # 小程序支付调用方式
            mchid=MCHID,
            private_key=PRIVATE_KEY,
            cert_serial_no=CERT_SERIAL_NO,
            apiv3_key=APIV3_KEY,
            appid=APPID,
            notify_url=NOTIFY_URL,  # 回调地址
            cert_dir=CERT_DIR,
            logger=LOGGER,
            partner_mode=False,
            proxy=None,
        )

    # 小程序支付
    def pay_miniprog(
        self, order_no: str, openid: str, func_name: str = "", amount: int = 0
    ):
        # 下单成功后，将prepay_id和其他必须的参数组合传递给小程序的wx.requestPayment接口唤起支付
        # 生成自定义订单号

        out_trade_no = order_no
        payer = {"openid": openid}
        # 构建支付信息，并调用微信接口，获取需要回传给小程序的预支付信息，
        code, message = self.__wxpay.pay(
            description=func_name,  # 所支付的商品或服务名称,由参数传入
            out_trade_no=out_trade_no,  # 生成自定义订单号
            amount={"total": amount},  # 订单总金额,转为分单位
            pay_type=WeChatPayType.MINIPROG,
            payer=payer,  # 支付的用户标识
        )
        # 检查调用结果
        result = json.loads(message)
        if code in range(200, 300):
            # 调用成功
            prepay_id = result.get("prepay_id")
            timestamp = str(int(datetime.datetime.now().timestamp()))  # 时间戳
            noncestr = uuid.uuid4().hex  # 32位随机字符串
            package = "prepay_id=" + prepay_id
            paysign = self.__wxpay.sign(
                data=[self.__weconfig.app_id, timestamp, noncestr, package],
                sign_type=SignType.RSA_SHA256,
            )
            signtype = "RSA"

            # 返回预支付信息
            return {
                "appId": self.__weconfig.app_id,
                "timeStamp": timestamp,
                "nonceStr": noncestr,
                "package": "prepay_id=%s" % prepay_id,
                "signType": signtype,
                "paySign": paysign,
            }

        else:
            raise APIException(
                StatusCodeEnum.WE_PAY_ERR,
                detail=result,
            )

    def get_openid(self, code, mp_config) -> str:
        """通过code码换取用户openid"""
        # 组装获取openid的url，并执行获取
        url = (
            "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"
            % (mp_config.app_id, mp_config.app_secret, code)
        )

        # 调用微信接口获取openid
        r_data = requests.get(url).json()
        print("登录",r_data)
        # 判断返回值异常
        if not r_data:
            raise APIException(StatusCodeEnum.WE_NODATA_ERR)
        if r_data.get("errcode"):
            raise APIException(StatusCodeEnum.WE_CODE_ERR, r_data)

        # 返回openid
        return r_data["openid"]

    def notify(self, request, callback):

        headers = {}
        headers.update(
            {"Wechatpay-Signature": request.META.get("HTTP_WECHATPAY_SIGNATURE")}
        )
        headers.update(
            {"Wechatpay-Timestamp": request.META.get("HTTP_WECHATPAY_TIMESTAMP")}
        )
        headers.update({"Wechatpay-Nonce": request.META.get("HTTP_WECHATPAY_NONCE")})
        headers.update({"Wechatpay-Serial": request.META.get("HTTP_WECHATPAY_SERIAL")})

        re_request_body = getattr(request, "_body", request.body)
        result = self.__wxpay.callback(headers, re_request_body)

        if result and result.get("event_type") == "TRANSACTION.SUCCESS":
            resp = result.get("resource")
            # appid = resp.get('appid')
            # mchid = resp.get('mchid')
            # out_trade_no = resp.get('out_trade_no')
            # transaction_id = resp.get('transaction_id')
            # trade_type = resp.get('trade_type')
            # trade_state = resp.get('trade_state')
            # trade_state_desc = resp.get('trade_state_desc')
            # bank_type = resp.get('bank_type')
            # attach = resp.get('attach')
            # success_time = resp.get('success_time')
            # payer = resp.get('payer')
            # amount = resp.get('amount').get('total')
            # TODO: 根据返回参数进行必要的业务处理，处理完后返回200或204
            if not callback(resp):
                response = JsonResponse({"code": "FAILED", "message": "失败"})
                response.status_code = 500
                return response
            return JsonResponse({"code": "SUCCESS", "message": "成功"})
        else:
            response = JsonResponse({"code": "FAILED", "message": "失败"})
            response.status_code = 500
            return response
